// Copyright (c) Microsoft Corporation.  All rights reserved.

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.ComponentModel;
using System.Security.Principal;

namespace SSISAccess
{
    internal class AccountInfo
    {
        private const int ERROR_SUCCESS = 0;
        private const int ERROR_INSUFFICIENT_BUFFER = 122;

        enum SID_NAME_USE
        {
            SidTypeUser = 1,
            SidTypeGroup,
            SidTypeDomain,
            SidTypeAlias,
            SidTypeWellKnownGroup,
            SidTypeDeletedAccount,
            SidTypeInvalid,
            SidTypeUnknown,
            SidTypeComputer
        }

        [DllImport("advapi32.dll", EntryPoint = "LookupAccountNameW", CharSet = CharSet.Unicode, SetLastError = true)]
        private static extern bool LookupAccountName(
            string computerName,
            string accountName,
            IntPtr binarySid,
            ref UInt32 binarySidLen,
            StringBuilder domainName,
            ref UInt32 domainNameLen,
            out SID_NAME_USE peUse);

        private SecurityIdentifier sid;
        private string accountName;
        private string domainName;
        private SID_NAME_USE use;
        private bool isDnsName;
        private string userName;

        private AccountInfo() { }

        public AccountInfo(string accountName)
        {
            this.accountName = accountName;
            int separator = this.accountName.IndexOf('\\');
            if (separator == -1)
            {
                separator = this.accountName.IndexOf('@');
                if (separator == -1)
                {
                    this.userName = this.accountName;
                }
                else
                {
                    this.isDnsName = true;
                }
            }

            if (separator != -1)
            {
                if (this.isDnsName)
                {
                    this.userName = this.accountName.Substring(0, separator - 1);
                }
                else
                {
                    this.userName = this.accountName.Substring(separator + 1);
                }
            }
        }

        public void Initialize()
        {
            int win32Error = ERROR_SUCCESS;
            StringBuilder domainNameBuffer = new StringBuilder();
            UInt32 domainNameLen = (UInt32)domainNameBuffer.Capacity;
            IntPtr unmanagedSid = IntPtr.Zero;

            try
            {
                int unmanagedSidLen = 1024;
                UInt32 sidLen = (UInt32)unmanagedSidLen;
                unmanagedSid = Marshal.AllocCoTaskMem(unmanagedSidLen);

                while (!LookupAccountName(null, this.accountName, unmanagedSid, ref sidLen, domainNameBuffer, ref domainNameLen, out this.use))
                {
                    win32Error = Marshal.GetLastWin32Error();
                    if (win32Error != ERROR_INSUFFICIENT_BUFFER)
                    {
                        break;
                    }

                    win32Error = ERROR_SUCCESS;
                    if (sidLen > unmanagedSidLen)
                    {
                        Marshal.FreeCoTaskMem(unmanagedSid);
                        unmanagedSidLen = (int)sidLen;
                        unmanagedSid = Marshal.AllocCoTaskMem(unmanagedSidLen);
                    }

                    domainNameBuffer.EnsureCapacity((int)domainNameLen);
                }

                if (win32Error != ERROR_SUCCESS)
                {
                    throw new Win32Exception(win32Error);
                }

                this.sid = new SecurityIdentifier(unmanagedSid);
                this.domainName = domainNameBuffer.ToString();
            }
            finally
            {
                Marshal.FreeCoTaskMem(unmanagedSid);
            }

        }

        public string AccountName { get { return this.accountName; } }
        public string DomainName { get { return this.domainName; } }
        public SecurityIdentifier Sid { get { return this.sid; } }
        public string UserName { get { return this.userName; } }
        public bool IsGroup { get { return this.use == SID_NAME_USE.SidTypeGroup || this.use == SID_NAME_USE.SidTypeAlias; } }
        public bool IsLocalGroup { get { return this.use == SID_NAME_USE.SidTypeAlias; } }
        public bool IsUser { get { return this.use == SID_NAME_USE.SidTypeUser; } }
    }
}
